/**
 * File: ThreadedPageParser.java
 * TCSS 422 Team Project 1
 * Autumn 2009
 * Team Members: Darin Cyr, Kirk Leonard, John Patanian
 */
package multiThread;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.net.MalformedURLException;
import java.net.URL;

import javax.swing.text.MutableAttributeSet;
import javax.swing.text.html.HTML;
import javax.swing.text.html.HTMLEditorKit;
import javax.swing.text.html.HTMLEditorKit.Parser;

import singlethread.HTMLParse;


/**
 * Handles all the parsing of web pages
 * Takes pages from the page buffer and looks for links.
 * Links found are added to the URLBucket
 * Refer to http://www.informit.com/articles/article.aspx?p=31059
 * @author Darin Cyr
 * @author Kirk Leonard
 * @author John Patanian
 * @version Autumn 2009
 */
public class ThreadedPageParser extends HTMLEditorKit.ParserCallback 
  implements Runnable  
{
  
  
/**
 *   
 */
  private static final int SLEEP_TIME_IN_MS = 40;
  
/**
 * Holds a reference to the Buffered web pages.
 */
  private final Buffer<String> my_pages;

  /**
   * Holds a reference to the parsed pages.
   */
  private final Buffer<String> my_parsed_pages;

  /**
   * Holds the visited and unvisited urls.
   */
  private final URLBucket my_bucket; 
  /**
   * Holds the parsed page text.
   */
  private StringBuffer my_output;

  /** The page's base url.
   * 
   */
  private URL my_url;
  
  /**
   * Creates a new ThreadedPageParser.
   * @param the_pages the buffered html pages
   * @param the_bucket Storage for visited and unvisited urls
   * @param the_parsed_pages the pages parsed with words only.
   */
  public ThreadedPageParser(final multiThread.URLBucket the_bucket, 
    final Buffer<String> the_pages,
    final Buffer<String> the_parsed_pages)
  {
    my_pages = the_pages;
    my_parsed_pages = the_parsed_pages;
    my_bucket = the_bucket;
    my_url = null;
  }


  /**
  * what to do with text.
  * text comes in bunches, so open a file once, and add text as it's found
  * @param the_text The text.
  * @param the_pos The position.
  */
  public void handleText(final char [] the_text, final int the_pos)
  {
    for (int i = 0; i < the_text.length; i++)
    {
      my_output.append(the_text[i]);
    }
    
    my_output.append("\n");

  }

  /**
  * @see javax.swint.text.html.HTMLEditorKit$ParserCallback#handleSimpleTag(javax.swing.text.html.HTML.Tag, javax.swing.text.MutableAttributeSet, int)
  * @param the_tag The html tag
  * @param the_attribute_set The html attribute set
  * @param the_pos The position.
  */
  public void handleSimpleTag(final HTML.Tag the_tag, 
    final MutableAttributeSet the_attribute_set,
    final int the_pos)
  {
    String href = (String) the_attribute_set.getAttribute(HTML.Attribute.HREF);
      
    if (href == null && the_tag.equals(HTML.Tag.FRAME))
    {
      href = (String) the_attribute_set.getAttribute(HTML.Attribute.SRC);
    }
      
    if (href == null || href.toLowerCase().startsWith("mailto:"))
    {
      return;
    }

    final int location = href.indexOf('#');
      
    if (location != -1)
    {
      href = href.substring(0, location);
    }
      
    handleLink(my_url, href);
  }


  /**
  * Simple tags are tags like <br> that do not have opening and closing tags.
  * Start tags are the first tags of pairs.
  * The <p> in a <p></p> pair, for example
  * @param the_tag The html tag.
  * @param the_attribute_set The attribute set.
  * @param the_pos The position.
  */
  public void handleStartTag(final HTML.Tag the_tag, 
    final MutableAttributeSet the_attribute_set,
    final int the_pos)
  {
    handleSimpleTag(the_tag, the_attribute_set, the_pos);    // handle the same way
  }

    
  /**
   * This handles when a url is found.
   * @param the_base_url The base url.
   * @param the_spec The spec
   */
  protected void handleLink(final URL the_base_url, final String the_spec)
  {
    URL a_url = null;
    
    try
    {
      a_url = new URL(the_base_url, the_spec);
    }
    catch (final MalformedURLException e)
    {
      return;
    }

    if (a_url != null)
    {
      try
      {
        my_bucket.add(a_url);
      }
      catch (final InterruptedException e)
      {
        return;
      }
    }

  }
    
    /**
     * Cache the file locally for later processing.
     */
  public void saveParsedPage()
  {
    my_output = new StringBuffer();
  }


  @Override
  public void run() 
  {
    try 
    {
      doParsePage();
    }  
    catch (final InterruptedException e) 
    {
      return;
    }
    
    try 
    {
      Thread.sleep((int) Math.random() * SLEEP_TIME_IN_MS); // sleep for a randomly chosen time
    } 
    catch (final InterruptedException e) 
    {
      return;
    }

    
  }
  
  /**
   * Parses a page from the URLBucket.
   * @throws InterruptedException .
   */
  private void doParsePage() throws InterruptedException
  {
    saveParsedPage();
    
    // As a consumer, gets from the page buffer.
    String the_page = my_pages.get();

    //the first line written was the url of the page
    final int second_line = the_page.indexOf("\n");
    
    
    final String url_string = the_page.substring(0, second_line - 1).trim();
    try 
    {
      my_url = new URL(url_string);
    } 
    catch (final MalformedURLException e2) 
    {
      // TODO Auto-generated catch block
      e2.printStackTrace();
    }
    
    //The rest of the page is the original html
    the_page = the_page.substring(second_line + 1);

    final Parser a_parser = new HTMLParse().getParser();
    
    final InputStream is;
    
    try 
    {
      is = new ByteArrayInputStream(the_page.getBytes("UTF-8"));
      a_parser.parse(new InputStreamReader(is), this, true);
      
      //as a Producer add to the parsed_page buffer.
      my_parsed_pages.add(my_output.toString());
    } 
    catch (final UnsupportedEncodingException e1) 
    {
      // TODO Auto-generated catch block
      e1.printStackTrace();
    }
    catch (final IOException e) 
    {
      e.printStackTrace();
    }
  }  
}
